/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许，不可做商业用途！
 *
 * 版权所有，侵权必究！
 */
package com.yami.shop.discount.api.service.impl;

import cn.hutool.core.util.BooleanUtil;
import com.yami.shop.bean.app.dto.ChooseDiscountItemDto;
import com.yami.shop.bean.app.dto.DiscountDto;
import com.yami.shop.bean.app.dto.ProductItemDto;
import com.yami.shop.bean.enums.DiscountRule;
import com.yami.shop.common.config.Constant;
import com.yami.shop.common.util.Arith;
import com.yami.shop.discount.api.dto.DiscountSumDto;
import com.yami.shop.discount.api.dto.DiscountSumItemDto;
import com.yami.shop.discount.api.service.DiscountManagerService;
import com.yami.shop.discount.common.model.Discount;
import com.yami.shop.discount.common.model.DiscountItem;
import com.yami.shop.discount.common.model.DiscountProd;
import com.yami.shop.discount.common.service.DiscountService;
import ma.glasnost.orika.MapperFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author yami
 */
@Service
public class DiscountManagerServiceImpl implements DiscountManagerService {

    @Autowired
    private DiscountService discountService;
    @Autowired
    private MapperFacade mapperFacade;


    /**
     * 方法的作用
     * 1.计算优惠金额
     * 2. 并往productItems 设置满减优惠信息
     *
     * @param shopId
     * @param productItems
     * @return
     */
    @Override
    public DiscountSumDto calculateDiscount(Long shopId, List<? extends ProductItemDto> productItems) {

        DiscountSumDto discountSumDto = new DiscountSumDto();

        List<Discount> discounts = discountService.listDiscountsAndItemsByShopId(shopId);

        // 旧的商品数据，也就是第一次传入的商品数据
        List<ProductItemDto> oldProdItems = new ArrayList<>(productItems);

        // 以优惠活动id为key， 每个满减活动的活动信息 为value的map
        // {优惠活动id：每个满减活动的活动信息}
        Map<Long, DiscountSumItemDto> discountIdDiscountSumItemMap = new HashMap<>(productItems.size());
        //保存满减的金额
        double amount = 0.0;
        if (productItems.size() == 1 && Objects.equals(productItems.get(0).getDiscountId(), 0L)) {
            // 立即提交订单, 选择满减最高的活动
            ProductItemDto productItemDto = productItems.get(0);
            for (Discount discount : discounts) {
                //判断商品是否参与该活动
                boolean hasDiscount = isHasDiscount(discount, productItemDto.getProdId());
                if (!hasDiscount) {
                    continue;
                }
                //添加到活动列表
                DiscountDto discountDto = new DiscountDto();
                discountDto.setDiscountName(discount.getDiscountName());
                discountDto.setDiscountId(discount.getDiscountId());
                productItemDto.getDiscounts().add(discountDto);
                //获取最高满减的活动
                DiscountSumItemDto discountSumItemDto = new DiscountSumItemDto();
                discountSumItemDto.setProdCount(productItemDto.getProdCount());
                if (BooleanUtil.isTrue(productItemDto.getIsChecked()) || Objects.isNull(productItemDto.getIsChecked())) {
                    discountSumItemDto.setProdsPrice(productItemDto.getProductTotalAmount());
                } else {
                    discountSumItemDto.setProdsPrice(0.00);
                }
                findDiscountItemAndGetReduceAmount(discount, discountSumItemDto);
                discountIdDiscountSumItemMap.put(discount.getDiscountId(), discountSumItemDto);
                //该活动的满减金额大于前一个，则把满减活动替换成当前的活动
                if (productItemDto.getDiscountId() == 0 || amount < discountSumItemDto.getReduceAmount()){
                    productItemDto.setDiscountId(discount.getDiscountId());
                    discountSumDto.setTotalReduceAmount(discountSumItemDto.getReduceAmount());
                    amount = discountSumItemDto.getReduceAmount();
                }
            }
        } else {
            //购物车
            for (Discount discount : discounts) {
                // 获取当前优惠的商品
                List<ProductItemDto> hasCurrentDiscountProds = mergeDiscountProd(oldProdItems, discount);

                DiscountSumItemDto discountSumItemDto = new DiscountSumItemDto();

                // 计算参与该活动的所有商品的商品金额，商品数量
                for (ProductItemDto hasDiscountProd : hasCurrentDiscountProds) {
                    if (Objects.isNull(hasDiscountProd.getIsChecked()) || hasDiscountProd.getIsChecked()){
                        discountSumItemDto.setProdCount(discountSumItemDto.getProdCount() + hasDiscountProd.getProdCount());
                        discountSumItemDto.setProdsPrice(Arith.add(discountSumItemDto.getProdsPrice(), hasDiscountProd.getProductTotalAmount()));
                    }
                }

                findDiscountItemAndGetReduceAmount(discount, discountSumItemDto);

                discountIdDiscountSumItemMap.put(discount.getDiscountId(), discountSumItemDto);

                discountSumDto.setTotalReduceAmount(Arith.add(discountSumDto.getTotalReduceAmount(), discountSumItemDto.getReduceAmount()));
            }
        }

        discountSumDto.setDiscountIdDiscountSumItemMap(discountIdDiscountSumItemMap);
        return discountSumDto;
    }

    @Override
    public ChooseDiscountItemDto getChooseDiscountItemDto(DiscountSumDto discountSumDto, Long discountId) {

        ChooseDiscountItemDto chooseDiscountItemDto = null;
        // 参与活动
        if (discountId != -1 && discountId != 0) {
            Map<Long, DiscountSumItemDto> discountIdDiscountSumItemMap = discountSumDto.getDiscountIdDiscountSumItemMap();
            DiscountSumItemDto discountSumItemDto = discountIdDiscountSumItemMap.get(discountId);
            if (discountSumItemDto == null) {
                return null;
            }

            Discount discount = discountService.getDiscountAndItemAndProdById(discountId);

            chooseDiscountItemDto = mapperFacade.map(discount, ChooseDiscountItemDto.class);

            chooseDiscountItemDto.setProdsPrice(discountSumItemDto.getProdsPrice());
            chooseDiscountItemDto.setProdCount(discountSumItemDto.getProdCount());
            chooseDiscountItemDto.setNeedAmount(discountSumItemDto.getNeedAmount());
            chooseDiscountItemDto.setDiscountItemId(discountSumItemDto.getDiscountItemId());
            chooseDiscountItemDto.setDiscount(discountSumItemDto.getDiscount());
            chooseDiscountItemDto.setReduceAmount(discountSumItemDto.getReduceAmount());
        }
        return chooseDiscountItemDto;
    }

    /**
     * 1. 找出当前优惠活动属于那个优惠项
     * 2. 计算满减
     * @param discount 优惠活动
     * @return 优惠金额
     */
    private DiscountSumItemDto findDiscountItemAndGetReduceAmount(Discount discount, DiscountSumItemDto discountSumItemDto) {
        // 枚举DiscountRule(0 满钱减钱 1满件减钱 2 满钱打折 3满件打折)
        Integer discountRule = discount.getDiscountRule();
        // 减免类型 0按满足最高层级减一次 1每满一次减一次
        Integer discountType = discount.getDiscountType();

        List<DiscountItem> discountItems = discount.getDiscountItems();

        double prodCount = discountSumItemDto.getProdCount();
        double prodsPrice = discountSumItemDto.getProdsPrice();

        Double totalReduceAmount = 0.0;
        Long discountItemId = -1L;
        for (DiscountItem discountItem : discountItems) {
            // 优惠（元/折）
            Double reduceAmount = discountItem.getDiscount();

            discountItemId = discountItem.getDiscountItemId();
            Double needAmount = discountItem.getNeedAmount();
            discountSumItemDto.setNeedAmount(needAmount);
            discountSumItemDto.setDiscount(reduceAmount);
            // 当规则为满钱减钱时
            if (Objects.equals(discountRule, DiscountRule.M2M.value()) && prodsPrice >= needAmount) {
                totalReduceAmount = getTotalReduceAmountBySubMoney(discountType, prodsPrice, reduceAmount, needAmount);
                break;
            }
            // 当规则为满件减钱时
            else if (Objects.equals(discountRule, DiscountRule.P2M.value()) && prodCount >= needAmount) {
                // 当商品价格大于最高层级时
                totalReduceAmount = getTotalReduceAmountBySubMoney(discountType, prodCount, reduceAmount, needAmount);
                break;
            }
            // 当规则为满钱打折时
            else if (Objects.equals(discountRule, DiscountRule.M2D.value()) && prodsPrice >= needAmount) {
                totalReduceAmount = Arith.mul(prodsPrice, Arith.sub(1, Arith.div(reduceAmount, 10L)));
                break;
            }
            // 当规则为满件打折时
            else if (Objects.equals(discountRule, DiscountRule.P2D.value()) && prodCount >= needAmount) {
                totalReduceAmount = Arith.mul(prodsPrice, Arith.sub(1, Arith.div(reduceAmount, 10L)));
                break;
            }
        }
        // 如果是每满一次减一次，则需要判断上限 不为满钱减钱或者满足最高层级减一次情况下，需要判断上限
        if(discountType != 0 || !Objects.equals(discount.getDiscountRule(),0)) {
            totalReduceAmount = Math.min(discount.getMaxReduceAmount(), totalReduceAmount);
        }
        totalReduceAmount = Arith.roundByBanker(totalReduceAmount,2);
        discountSumItemDto.setReduceAmount(totalReduceAmount);
        // 如果满减折扣小于0.01元，就不要给商品满减
        if (Double.compare(discountSumItemDto.getReduceAmount(), Constant.MIN_PRODUCT_AMOUNT) < 0) {
            discountSumItemDto.setReduceAmount(0.0);
        }
        discountSumItemDto.setDiscountItemId(discountItemId);
        return discountSumItemDto;
    }

    private Double getTotalReduceAmountBySubMoney(Integer discountType, double prodsPriceOrCount, Double reduceAmount, Double needAmount) {
        Double totalReduceAmount;
        if (discountType == 0) {
            // 当商品价格大于最高层级时
            totalReduceAmount = reduceAmount;
        } else {
            // 查看满足多少次条件
            int multi = (int) Math.floor(Arith.div(prodsPriceOrCount, needAmount));
            totalReduceAmount = Arith.mul(reduceAmount, multi);
        }
        return totalReduceAmount;
    }

    /**
     * 合并当前优惠的商品
     * 注：如果oldProdItems商品有该优惠，则将其抽取出来，并且在oldProdItems中移除该商品
     *
     * @param oldProdItems 商品项
     * @param discount     活动
     * @return
     */
    private List<ProductItemDto> mergeDiscountProd(List<ProductItemDto> oldProdItems, Discount discount) {

        // 拥有当前优惠活动的商品
        List<ProductItemDto> hasCurrentDiscountProds = new ArrayList<>();

        for (ProductItemDto productItem : oldProdItems) {
            // 查看商品是否包含优惠活动
            boolean hasDiscount = isHasDiscount(discount, productItem.getProdId());
            if (hasDiscount) {
                DiscountDto discountDto = new DiscountDto();
                discountDto.setDiscountId(discount.getDiscountId());
                discountDto.setDiscountName(discount.getDiscountName());
                productItem.getDiscounts().add(discountDto);
            }

            // 商品选择了这个活动
            boolean prodChooseDiscount = Objects.equals(productItem.getDiscountId(), discount.getDiscountId());

            // 如果商品不包含该优惠活动
            // 或用户选择不参与该活动，将活动清除
            if (!hasDiscount && prodChooseDiscount) {
                //  将往productItems 设置满减优惠信息为【-1】
                productItem.setDiscountId(-1L);
            }
            // 如果 商品参与了该活动，将会分为几个步骤
            // 判断商品是否在这个活动中（如果用户没有为商品主动选择优惠活动，或选择的优惠活动与该满减的优惠活动一样 则视为商品在活动中）
            else if (hasDiscount && prodChooseDiscount) {

                // 2. 将往productItems 设置满减优惠信息为【当前活动】
                productItem.setDiscountId(discount.getDiscountId());

                // 3. 将该活动的商品统一起来
                hasCurrentDiscountProds.add(productItem);
            }
            // 如果商品参与该活动
            // 且用户没有主动放弃该活动
            else if (hasDiscount && productItem.getDiscountId() == 0) {

                // 2. 将往productItems 设置满减优惠信息为【当前活动】
                productItem.setDiscountId(discount.getDiscountId());

                // 3. 将该活动的商品统一起来
                hasCurrentDiscountProds.add(productItem);
            }
        }

        return hasCurrentDiscountProds;
    }

    /**
     * 查看商品是否包含优惠活动
     *
     * @param discount 当前优惠活动信息
     * @param prodId   当前商品id
     * @return
     */
    private boolean isHasDiscount(Discount discount, Long prodId) {
        boolean hasDiscount = false;
        List<DiscountProd> discountProds = discount.getDiscountProds();
        // 所有商品都含有该优惠
        if (discount.getSuitableProdType() == 0) {
            hasDiscount = true;
        } else if (discount.getSuitableProdType() == 1) {
            // 指定商品含有该优惠
            for (DiscountProd discountProd : discountProds) {
                if (Objects.equals(discountProd.getProdId(), prodId)) {
                    hasDiscount = true;
                }
            }
        } else {
            // 指定商品不含有该优惠
            for (DiscountProd discountProd : discountProds) {
                if (Objects.equals(discountProd.getProdId(), prodId)) {
                    hasDiscount = true;
                }
            }
            hasDiscount = !hasDiscount;
        }
        return hasDiscount;
    }


}
